//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

namespace Microsoft.Research.ScientificWorkflow.UIDesigner
{
    #region  Namespace
    using System;
    using System.Collections.Generic;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Data;
    using System.Windows.Media;
    using Microsoft.Research.ScientificWorkflow.TridentModel;
    using Microsoft.Research.ScientificWorkflow.TridentUtilities;
    using Microsoft.Research.DataLayer;
    using TridentAPI;
    using Microsoft.Research.ScientificWorkflow.TridentComposer;

    #endregion

    /// <summary>
    /// Interaction logic for ActivityParameterDesigner
    /// </summary>
    public class ActivityParameterDesigner : Grid, IBindingEndpoint
    {
        #region Declaration

        /// <summary>
        /// Holds the WorkflowActivityElement
        /// </summary>
        private TridentUIActivityElement parent;

        /// <summary>
        /// Holds the ParameterDescriptionModel 
        /// </summary>
        private ParameterDescriptionModel parameterModel;

        /// <summary>
        /// Holds the Brush Color for Optional parameter
        /// </summary>
        private SolidColorBrush optionalParameterFillBrush;

        /// <summary>
        /// Holds the Brush Color Required parameter
        /// </summary>
        private SolidColorBrush parameterFillBrush;

        /// <summary>
        /// Holds the Parameter Type
        /// </summary>
        private ParameterFlowType parameterType;

        /// <summary>
        /// Holds the activity model.
        /// </summary>
        protected BaseModel Model
        {
            get;
            set;
        }

        /// <summary>
        /// If conditional param.
        /// </summary>
        private bool isConditionalParameter;

        #endregion

        #region Constructor

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="param">ParameterDescriptionModel</param>
        /// <param name="parent">WorkflowActivityElement</param>
        /// <param name="paramType">ParameterFlowType</param>
        public ActivityParameterDesigner(ParameterDescriptionModel parameter, ParameterFlowType parameterType, BaseModel model)
        {
            this.isConditionalParameter = true;
            this.Height = 8;
            this.Width = 8;
            this.parameterModel = parameter;
            this.parameterType = parameterType;
            this.Model = model;
        }

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="param">ParameterDescriptionModel</param>
        /// <param name="parent">WorkflowActivityElement</param>
        /// <param name="paramType">ParameterFlowType</param>
        public ActivityParameterDesigner(ParameterDescriptionModel parameter, TridentUIActivityElement parent, ParameterFlowType parameterType, BaseModel model)
        {
            this.Height = 25;
            ColumnDefinition firstColumn = new ColumnDefinition();
            firstColumn.Width = new GridLength(20.011);
            ColumnDefinition secondColumn = new ColumnDefinition();
            secondColumn.Width = new GridLength(1, GridUnitType.Star);

            if (ParameterFlowType.InputParameter == parameterType)
            {
                Margin = new Thickness(-10, 0, 0, 0);
                ColumnDefinitions.Add(firstColumn);
                ColumnDefinitions.Add(secondColumn);
            }
            else
            {
                Margin = new Thickness(0, 0, -10, 0);
                ColumnDefinitions.Add(secondColumn);
                ColumnDefinitions.Add(firstColumn);
            }

            this.parameterModel = parameter;
            this.parameterType = parameterType;
            this.parent = parent;
            this.Model = model;
            this.optionalParameterFillBrush = new SolidColorBrush(Colors.Transparent);
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets and sets the color for the parameter triangle.
        /// </summary>
        public SolidColorBrush ParamFillBrush
        {
            get
            {
                return this.parameterFillBrush;
            }

            set
            {
                this.parameterFillBrush = value;
            }
        }

        /// <summary>
        /// Gets the activity model.
        /// </summary>
        public BaseModel ActModel
        {
            get { return Model; }
        }

        /// <summary>
        /// The parent activity element of the parameter.
        /// </summary>
        public TridentUIActivityElement ParentActivityElement
        {
            get { return parent; }
            set { parent = value; }
        }

        /// <summary>
        /// The parameter model.
        /// </summary>
        public ParameterDescriptionModel ParamModel
        {
            get
            {
                return this.parameterModel;
            }
        }

        /// <summary>
        /// The parameter control.
        /// </summary>
        public ParameterControl ParameterCtrl { get; set; }

        #endregion Properties

        #region Public Methods

        /// <summary>
        /// Creates Tooltip for Parameter
        /// </summary>
        public static StackPanel CreateParameterTooltip(ParameterDescriptionModel parameterModel)
        {
            StackPanel topPanel = new StackPanel();
            TextBlock header = new TextBlock();
            TextBlock data = new TextBlock();
            TextBlock value = new TextBlock();
            TextBlock outputLabel = new TextBlock();

            TextBlock description = new TextBlock();

            header.FontWeight = FontWeights.Bold;
            header.Text = parameterModel.PropertyName;

            data.FontStyle = FontStyles.Italic;
            if (null != parameterModel.DataType)
            {
                data.Text = parameterModel.DataType.ToString();
            }

            try
            {
                if (!parameterModel.IsDatabound)
                {
                    TypeInitializer typeInit = TypeProviderHelper.FetchTypeInitializer(parameterModel.DataType, Runtime.WPF);
                    if (typeInit != null)
                    {
                        string deserializedVal = (parameterModel.Value != null) ? parameterModel.Value.ToString() : string.Empty;
                        BaseTypeInitializer baseTypeinit = ActivityComposer.LoadInitializer(typeInit, deserializedVal);
                        if (baseTypeinit != null)
                        {
                            value.Text = baseTypeinit.GetStringValue();
                        }
                    }
                    else
                    {
                        // Bind the Text Value TextBlock with paramModel.Value
                        Binding valueBinding = new Binding("Value");
                        valueBinding.Source = parameterModel;
                        value.SetBinding(TextBlock.TextProperty, valueBinding);
                    }
                }
            }
            catch (TridentCustomException ex)
            {
                TridentErrorHandler.HandleTridentCustomExceptionInUI(ex);
            }
            
            if (null != parameterModel.Description)
            {
                description.Text = parameterModel.Description;
            }

            topPanel.Children.Add(header);

            // Add the  value if its Input Parameters
            if (parameterModel.IsInputParameter)
            {
                topPanel.Children.Add(value);
            }

            if (parameterModel.IsOutputParameter)
            {
                Binding outputLabelBinding = new Binding("OutputLabel");
                outputLabelBinding.Source = parameterModel;

                outputLabel.SetBinding(TextBlock.TextProperty, outputLabelBinding);

                topPanel.Children.Add(outputLabel);
            }

            topPanel.Children.Add(data);
            topPanel.Children.Add(description);
            return topPanel;
        }

        /// <summary>
        /// Adds trigangle Image to the Grid
        /// </summary>
        public void AddParameterShape()
        {
            ParameterControl paramTriangle = new ParameterControl(this.parameterType, this.parent, this.parameterModel);
            this.ParameterCtrl = paramTriangle;
            Dictionary<string, object> dataHolder = new Dictionary<string, object>(2);
            dataHolder.Add("ParameterDescriptionModel", this.parameterModel);
            dataHolder.Add("ActivityModel", this.Model);
            paramTriangle.DataContext = dataHolder;
            paramTriangle.ToolTip = ActivityParameterDesigner.CreateParameterTooltip(this.parameterModel);
            this.AssignParameterBrush();
            this.Children.Add(paramTriangle);
            if (ParameterFlowType.InputParameter == this.parameterType)
            {
                Grid.SetColumn((UIElement)paramTriangle, 0);
            }
            else
            {
                Grid.SetColumn((UIElement)paramTriangle, 1);
            }
        }

        /// <summary>
        /// Assigns appropriate parameter coloring brush for the parameter model.
        /// </summary>
        public void AssignParameterBrush()
        {
            if (this.parameterModel.IsMandatory)
            {
                // Fill the triangle with the color corresponding to the parameter.
                this.ParameterCtrl.ParameterShapeFillBrush = this.ParamFillBrush;
                this.ParameterCtrl.ParameterShapeStrokeBrush = this.parameterFillBrush;
                if (this.parameterModel.IsMandatoryForWorkflow)
                {
                    // If the parameter is mandatory for the workflow then color the ellipse.
                    this.ParameterCtrl.EllipseFillColor = this.ParamFillBrush;
                    this.ParameterCtrl.TriangleBorderColor = Brushes.White;
                }
                else
                {
                    // Else use the default color.
                    this.ParameterCtrl.EllipseFillColor = this.ParameterCtrl.DefaultEllipseFillBrush;
                    this.ParameterCtrl.TriangleBorderColor = Brushes.Transparent;
                }
            }
            else
            {
                this.ParameterCtrl.TriangleBorderColor = Brushes.Transparent;

                // If the parameter is mandatory for the workflow then color the ellipse.
                if (this.parameterModel.IsMandatoryForWorkflow)
                {
                    this.ParameterCtrl.EllipseFillColor = this.parameterFillBrush;
                    this.ParameterCtrl.ParameterShapeFillBrush = this.ParameterCtrl.DefaultEllipseFillBrush;
                    this.ParameterCtrl.ParameterShapeStrokeBrush = Brushes.Transparent;
                }
                else
                {
                    this.ParameterCtrl.EllipseFillColor = this.ParameterCtrl.DefaultEllipseFillBrush;
                    this.ParameterCtrl.ParameterShapeFillBrush = this.optionalParameterFillBrush;
                    this.ParameterCtrl.ParameterShapeStrokeBrush = this.parameterFillBrush;
                }
            }
        }

        /// <summary>
        /// Adds trigangle Image to the Grid
        /// </summary>
        public void AddConditionParameterShape()
        {
            ConditionParamControl paramEllipse = new ConditionParamControl(this.parameterModel,this.parameterType,this.Model);
            Dictionary<string, object> dataHolder = new Dictionary<string, object>(2);
            dataHolder.Add("ParameterDescriptionModel", this.parameterModel);
            dataHolder.Add("ActivityModel", this.Model);
            paramEllipse.DataContext = dataHolder;

            this.Children.Add(paramEllipse);
            if (ParameterFlowType.InputParameter == this.parameterType)
            {
                Grid.SetColumn((UIElement)paramEllipse, 0);
            }
            else
            {
                Grid.SetColumn((UIElement)paramEllipse, 0);
            }
        }

        /// <summary>
        /// Adds parameter element text block to the Grid
        /// </summary>
        /// <param name="paramText"></param>
        public void AddParameterElementTextBlock(string parameterText)
        {
            TextBlock paramElementText = new TextBlock();
            if (!string.IsNullOrEmpty(parameterText))
            {
                paramElementText.Text = parameterText;
            }
            else
            {
                paramElementText.Text = string.Empty;
            }

            paramElementText.FontWeight = FontWeights.DemiBold;
            paramElementText.HorizontalAlignment = HorizontalAlignment.Stretch;
            paramElementText.VerticalAlignment = VerticalAlignment.Center;
            paramElementText.Foreground = Brushes.Black;
            paramElementText.TextWrapping = TextWrapping.Wrap;
            this.Children.Add(paramElementText);
            if (ParameterFlowType.InputParameter == this.parameterType)
            {
                paramElementText.Margin = new Thickness(3, -5, 10, 0);
                Grid.SetColumn((UIElement)paramElementText, 1);
            }
            else
            {
                paramElementText.Margin = new Thickness(10, -5, 3, 0);
                Grid.SetColumn((UIElement)paramElementText, 0);
            }
        }

        #endregion

        #region IBindingConnector Members

        /// <summary>
        /// The position where the binding connection has to start at.
        /// </summary>
        /// <param name="relativeTo">The element relative to which the Position is to be determined.</param>
        /// <returns>The connection point.</returns>
        public Point GetBindingStartPoint(UIElement relativeTo)
        {
            if (relativeTo == null)
            {
                throw new ArgumentNullException("relativeTo");
            }
            if (!this.isConditionalParameter)
            {
                return this.TranslatePoint(new Point(this.ActualWidth - 2.0, this.ActualHeight / 2.0), relativeTo);
            }
            else
            {
                return this.TranslatePoint(new Point(this.ActualWidth, this.ActualHeight / 2.0), relativeTo);
            }
        }

        /// <summary>
        /// The position where the binding connection has to end at.
        /// </summary>
        /// <param name="relativeTo">The element relative to which the Position is to be determined.</param>
        /// <returns>The connection point.</returns>
        public Point GetBindingEndpoint(UIElement relativeTo)
        {
            if (relativeTo == null)
            {
                throw new ArgumentNullException("relativeTo");
            }

            if (!this.isConditionalParameter)
            {
                return this.TranslatePoint(new Point(2, this.ActualHeight / 2.0), relativeTo);
            }
            else
            {

                return this.TranslatePoint(new Point(0, this.ActualHeight / 2.0), relativeTo);
            }
        }

        #endregion
    }
}
